package com.yl.nettylearning.chat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * @author yangl
 * 1、编写一个NIO群聊系统，实现服务器端和客户端的简单通讯（非阻塞）
 * 2、实现多人群聊
 * 3、服务器可以监测用户上线，离线，并实现消息转发功能
 * 4、客户端通过channel可以无阻塞发送消息给其他所有用户，同时可以接受其他用户发送的消息（由服务器转发得到）
 *  先编写服务器端
 *      1、服务器端启动并监听6667
 *      2、服务器接受客户端消息，并实现转发
 *  编写客户端
 *      1、连接客户端
 *      2、发送消息
 *      3、接收消息
 */
public class GroupChatServer {
    //定义属性
    private Selector selector;
    private ServerSocketChannel listenChannel;
    private static final int PORT = 6667;

    public GroupChatServer() {
        try{
            //得到选择器
            selector = Selector.open();
            //服务器端通道
            listenChannel = ServerSocketChannel.open();
            //绑定端口
            listenChannel.socket().bind(new InetSocketAddress(PORT));
            //设置非阻塞模式
            listenChannel.configureBlocking(false);
            //将listenChannel注册到selector上
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public void listen(){
        try{
            while (true){
                int count = selector.select();
                //遍历得到SelectionKey集合
                if(count > 0){
                    //说明有事件处理
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()){
                        SelectionKey key = iterator.next();
                        //监听到accept
                        if(key.isAcceptable()){
                            SocketChannel sc = listenChannel.accept();
                            sc.configureBlocking(false);
                            //注册到selector
                            sc.register(selector, SelectionKey.OP_READ);
                            //给出提示，xxx上线
                            System.out.println(sc.getRemoteAddress() + "上线了。。。");
                        }
                        //通道可读状态
                        if(key.isReadable()){
                            readData(key);
                        }
                        //删除当前的key，防止重复处理
                        iterator.remove();
                    }
                }else{
                    System.out.println("没有连接，等待中。。。。");
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    //读取客户端消息
    public void readData(SelectionKey key){
        SocketChannel channel = null;
        try {
            //取到关联的channel
            channel = (SocketChannel) key.channel();
            //创建buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int count = channel.read(buffer);
            //根据count的值处理
            if(count > 0){
                //如果有数据，就把缓冲区的数据转成字符串
                String msg = new String(buffer.array());
                //输出消息
                System.out.println("接收到来自客户端的消息：" + msg);
                //向其他客户端转发消息,并排除掉自己
                sendInfoToOthers(msg, channel);
            }
        }catch (IOException e){
            try {
                System.out.println(channel.getRemoteAddress() + "离线了。。。");
                //离线之后，要取消注册，
                key.cancel();
                //关闭通道
                channel.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

    //转发消息给其他客户端
    public void sendInfoToOthers(String msg, SocketChannel self){
        System.out.println("服务器转发消息中");
        //遍历 所有注册到selector上面的SocketChannel，并排除自己
        for (SelectionKey key : selector.keys()) {
            //通过key取出对应的socketChannel
            Channel targetChannel = key.channel();
            //排除自己
            if(targetChannel instanceof SocketChannel && targetChannel != self){
                //
                SocketChannel dest = (SocketChannel)targetChannel;
                //将msg存入buffer
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                //将buffer数据写入channel
                try {
                    dest.write(buffer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }
}
